package skiree.host.abetsy.api.annotation;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.thread.ExecutorBuilder;
import lombok.Data;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Component
@Data
public class ThreadPoolUtil {

    private static ThreadPoolUtil instance;

    private ThreadPoolExecutor executor;

    private static int corePoolSize = 2;

    private static int maxPoolSize = 4;

    private Map<Integer, EventQueueModel> eventQueueWaiting = new HashMap<>();

    private Map<EventQueueModel, Future<?>> eventQueueDoing = new HashMap<>();

    private Map<String, Object> info = new HashMap<>();

    private ThreadPoolUtil() {
    }

    public static ThreadPoolUtil getInstance() {
        if (instance == null) {
            synchronized (ThreadPoolUtil.class) {
                if (instance == null) {
                    instance = new ThreadPoolUtil();
                    instance.eventQueueWaiting = new ConcurrentHashMap<>();
                    instance.eventQueueDoing = new ConcurrentHashMap<>();
                    instance.info = new ConcurrentHashMap<>();
                    instance.executor = ExecutorBuilder.create().setCorePoolSize(corePoolSize).setMaxPoolSize(maxPoolSize).build();
                }
            }
        }
        return instance;
    }

    public void addToEventQueue(EventQueueModel queueModel) {
        this.eventQueueWaiting.put(queueModel.getId(), queueModel);
    }

    public void SettingExecutor(int coreSize, int maxSize) {
        corePoolSize = coreSize;
        maxPoolSize = maxSize;
        this.executor.setCorePoolSize(coreSize);
        this.executor.setMaximumPoolSize(maxSize);
    }

    public int getMaxSize() {
        return maxPoolSize;
    }

    public void computePoolInfo() {
        List<EventQueueModel> eventData = new ArrayList<>();
        List<EventQueueModel> doingList = this.eventQueueDoing.keySet().stream().sorted(Comparator.comparing(EventQueueModel::getId)).collect(Collectors.toList());
        List<EventQueueModel> waitingList = this.eventQueueWaiting.values().stream().sorted(Comparator.comparing(EventQueueModel::getId)).collect(Collectors.toList());
        eventData.addAll(doingList);
        eventData.addAll(waitingList);
        if (this.info.isEmpty()) {
            // 首次计算
            this.info.put("doingSizeThenBefore", 0);
            this.info.put("waitingSizeThenBefore", 0);
            this.info.put("activeSizeThenBefore", 0);
            this.info.put("completedSizeThenBefore", 0);
        } else {
            this.info.put("doingSizeThenBefore", doingList.size() - MapUtil.getInt(this.info, "doingSize"));
            this.info.put("waitingSizeThenBefore", waitingList.size() - MapUtil.getInt(this.info, "waitingSize"));
            this.info.put("activeSizeThenBefore", ThreadPoolUtil.getInstance().getExecutor().getActiveCount() - MapUtil.getInt(this.info, "activeSize"));
            this.info.put("completedSizeThenBefore", ThreadPoolUtil.getInstance().getExecutor().getCompletedTaskCount() - MapUtil.getInt(this.info, "completedSize"));
        }
        // 固定参数
        this.info.put("eventData", eventData);
        this.info.put("serveDate", DateUtil.format(new Date(), "yyyy-MM-dd"));
        this.info.put("serveTime", DateUtil.format(new Date(), "HH:mm:ss"));
        this.info.put("coreSize", ThreadPoolUtil.getInstance().getExecutor().getCorePoolSize());
        this.info.put("maxSize", ThreadPoolUtil.getInstance().getExecutor().getMaximumPoolSize());
        // 易变参数
        this.info.put("doingSize", doingList.size());
        this.info.put("waitingSize", waitingList.size());
        this.info.put("activeSize", ThreadPoolUtil.getInstance().getExecutor().getActiveCount());
        this.info.put("completedSize", ThreadPoolUtil.getInstance().getExecutor().getCompletedTaskCount());
    }

    public Map<String, Object> getPoolInfo(){
        return this.info;
    }

}
